/**
 ******************************************************************************
 * @file    stm32f4xx_ll_sdmmc.c
 * @author  MCD Application Team
 * @version V1.3.2
 * @date    26-June-2015
 * @brief   SDMMC Low Layer HAL module driver.
 *
 *          This file provides firmware functions to manage the following
 *          functionalities of the SDMMC peripheral:
 *           + Initialization/de-initialization functions
 *           + I/O operation functions
 *           + Peripheral Control functions
 *           + Peripheral State functions
 *
 * @verbatim
 * ==============================================================================
 ##### SDMMC peripheral features #####
 #####==============================================================================
 #####[..] The SD/SDIO MMC card host interface (SDIO) provides an interface between the APB2
 #####   peripheral bus and MultiMedia cards (MMCs), SD memory cards, SDIO cards and CE-ATA
 #####   devices.
 #####
 #####[..] The SDIO features include the following:
 #####   (+) Full compliance with MultiMedia Card System Specification Version 4.2. Card support
 #####       for three different databus modes: 1-bit (default), 4-bit and 8-bit
 #####   (+) Full compatibility with previous versions of MultiMedia Cards (forward compatibility)
 #####   (+) Full compliance with SD Memory Card Specifications Version 2.0
 #####   (+) Full compliance with SD I/O Card Specification Version 2.0: card support for two
 #####       different data bus modes: 1-bit (default) and 4-bit
 #####   (+) Full support of the CE-ATA features (full compliance with CE-ATA digital protocol
 #####       Rev1.1)
 #####   (+) Data transfer up to 48 MHz for the 8 bit mode
 #####   (+) Data and command output enable signals to control external bidirectional drivers.
 #####
 #####
 ##### How to use this driver #####
 #####==============================================================================
 #####[..]
 #####This driver is a considered as a driver of service for external devices drivers
 #####that interfaces with the SDIO peripheral.
 #####According to the device used (SD card/ MMC card / SDIO card ...), a set of APIs
 #####is used in the device's driver to perform SDIO operations and functionalities.
 #####
 #####This driver is almost transparent for the final user, it is only used to implement other
 #####functionalities of the external device.
 #####
 #####[..]
 #####(+) The SDIO clock (SDIOCLK = 48 MHz) is coming from a specific output of PLL
 #####    (PLL48CLK). Before start working with SDIO peripheral make sure that the
 #####    PLL is well configured.
 #####    The SDIO peripheral uses two clock signals:
 #####    (++) SDIO adapter clock (SDIOCLK = 48 MHz)
 #####    (++) APB2 bus clock (PCLK2)
 #####
 #####    -@@- PCLK2 and SDIO_CK clock frequencies must respect the following condition:
 #####         Frequency(PCLK2) >= (3 / 8 x Frequency(SDIO_CK))
 #####
 #####(+) Enable/Disable peripheral clock using RCC peripheral macros related to SDIO
 #####    peripheral.
 #####
 #####(+) Enable the Power ON State using the SDIO_PowerState_ON(SDIOx)
 #####    function and disable it using the function SDIO_PowerState_OFF(SDIOx).
 #####
 #####(+) Enable/Disable the clock using the __SDIO_ENABLE()/__SDIO_DISABLE() macros.
 #####
 #####(+) Enable/Disable the peripheral interrupts using the macros __SDIO_ENABLE_IT(hsdio, IT)
 #####    and __SDIO_DISABLE_IT(hsdio, IT) if you need to use interrupt mode.
 #####
 #####(+) When using the DMA mode
 #####    (++) Configure the DMA in the MSP layer of the external device
 #####    (++) Active the needed channel Request
 #####    (++) Enable the DMA using __SDIO_DMA_ENABLE() macro or Disable it using the macro
 #####         __SDIO_DMA_DISABLE().
 #####
 #####(+) To control the CPSM (Command Path State Machine) and send
 #####    commands to the card use the SDIO_SendCommand(SDIOx),
 #####    SDIO_GetCommandResponse() and SDIO_GetResponse() functions. First, user has
 #####    to fill the command structure (pointer to SDIO_CmdInitTypeDef) according
 #####    to the selected command to be sent.
 #####    The parameters that should be filled are:
 #####     (++) Command Argument
 #####     (++) Command Index
 #####     (++) Command Response type
 #####     (++) Command Wait
 #####     (++) CPSM Status (Enable or Disable).
 #####
 #####    -@@- To check if the command is well received, read the SDIO_CMDRESP
 #####        register using the SDIO_GetCommandResponse().
 #####        The SDIO responses registers (SDIO_RESP1 to SDIO_RESP2), use the
 #####        SDIO_GetResponse() function.
 #####
 #####(+) To control the DPSM (Data Path State Machine) and send/receive
 #####     data to/from the card use the SDIO_DataConfig(), SDIO_GetDataCounter(),
 #####    SDIO_ReadFIFO(), DIO_WriteFIFO() and SDIO_GetFIFOCount() functions.
 #####
 *** Read Operations ***
 ***=======================
 ***[..]
 ***  (#) First, user has to fill the data structure (pointer to
 ***      SDIO_DataInitTypeDef) according to the selected data type to be received.
 ***      The parameters that should be filled are:
 ***       (++) Data Timeout
 ***       (++) Data Length
 ***       (++) Data Block size
 ***       (++) Data Transfer direction: should be from card (To SDIO)
 ***       (++) Data Transfer mode
 ***       (++) DPSM Status (Enable or Disable)
 ***
 ***  (#) Configure the SDIO resources to receive the data from the card
 ***      according to selected transfer mode (Refer to Step 8, 9 and 10).
 ***
 ***  (#) Send the selected Read command (refer to step 11).
 ***
 ***  (#) Use the SDIO flags/interrupts to check the transfer status.
 ***
 *** Write Operations ***
 ***========================
 ***[..]
 *** (#) First, user has to fill the data structure (pointer to
 ***     SDIO_DataInitTypeDef) according to the selected data type to be received.
 ***     The parameters that should be filled are:
 ***      (++) Data Timeout
 ***      (++) Data Length
 ***      (++) Data Block size
 ***      (++) Data Transfer direction:  should be to card (To CARD)
 ***      (++) Data Transfer mode
 ***      (++) DPSM Status (Enable or Disable)
 ***
 *** (#) Configure the SDIO resources to send the data to the card according to
 ***     selected transfer mode.
 ***
 *** (#) Send the selected Write command.
 ***
 *** (#) Use the SDIO flags/interrupts to check the transfer status.
 ***
 ***@endverbatim
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *   1. Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice,
 *      this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 *   3. Neither the name of STMicroelectronics nor the names of its contributors
 *      may be used to endorse or promote products derived from this software
 *      without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"

/** @addtogroup STM32F4xx_HAL_Driver
 * @{
 */

/** @defgroup SDMMC_LL SDMMC Low Layer
 * @brief Low layer module for SD and MMC driver
 * @{
 */

#if defined( HAL_SD_MODULE_ENABLED ) || defined( HAL_MMC_MODULE_ENABLED )

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/** @defgroup SDMMC_LL_Exported_Functions SDMMC_LL Exported Functions
 * @{
 */

/** @defgroup HAL_SDMMC_LL_Group1 Initialization/de-initialization functions
 *  @brief    Initialization and Configuration functions
 *
 * @verbatim
 * ===============================================================================
 ##### Initialization/de-initialization functions #####
 #####===============================================================================
 #####[..]  This section provides functions allowing to:
 #####
 #####@endverbatim
 * @{
 */

/**
 * @brief  Initializes the SDIO according to the specified
 *         parameters in the SDIO_InitTypeDef and create the associated handle.
 * @param  SDIOx Pointer to SDIO register base
 * @param  Init SDIO initialization structure
 * @retval HAL status
 */
    HAL_StatusTypeDef SDIO_Init( SDIO_TypeDef * SDIOx,
                                 SDIO_InitTypeDef Init )
    {
        uint32_t tmpreg = 0;

        /* Check the parameters */
        assert_param( IS_SDIO_ALL_INSTANCE( SDIOx ) );
        assert_param( IS_SDIO_CLOCK_EDGE( Init.ClockEdge ) );
        assert_param( IS_SDIO_CLOCK_BYPASS( Init.ClockBypass ) );
        assert_param( IS_SDIO_CLOCK_POWER_SAVE( Init.ClockPowerSave ) );
        assert_param( IS_SDIO_BUS_WIDE( Init.BusWide ) );
        assert_param( IS_SDIO_HARDWARE_FLOW_CONTROL( Init.HardwareFlowControl ) );
        assert_param( IS_SDIO_CLKDIV( Init.ClockDiv ) );

        /* Set SDIO configuration parameters */
        tmpreg |= ( Init.ClockEdge |           \
                    Init.ClockBypass |         \
                    Init.ClockPowerSave |      \
                    Init.BusWide |             \
                    Init.HardwareFlowControl | \
                    Init.ClockDiv
                    );

        /* Write to SDIO CLKCR */
        MODIFY_REG( SDIOx->CLKCR, CLKCR_CLEAR_MASK, tmpreg );

        return HAL_OK;
    }

/**
 * @}
 */

/** @defgroup HAL_SDMMC_LL_Group2 I/O operation functions
 *  @brief   Data transfers functions
 *
 * @verbatim
 * ===============================================================================
 ##### I/O operation functions #####
 #####===============================================================================
 #####[..]
 #####This subsection provides a set of functions allowing to manage the SDIO data
 #####transfers.
 #####
 #####@endverbatim
 * @{
 */

/**
 * @brief  Read data (word) from Rx FIFO in blocking mode (polling)
 * @param  SDIOx Pointer to SDIO register base
 * @retval HAL status
 */
    uint32_t SDIO_ReadFIFO( SDIO_TypeDef * SDIOx )
    {
        /* Read data from Rx FIFO */
        return( SDIOx->FIFO );
    }

/**
 * @brief  Write data (word) to Tx FIFO in blocking mode (polling)
 * @param  SDIOx Pointer to SDIO register base
 * @param  pWriteData pointer to data to write
 * @retval HAL status
 */
    HAL_StatusTypeDef SDIO_WriteFIFO( SDIO_TypeDef * SDIOx,
                                      uint32_t * pWriteData )
    {
        /* Write data to FIFO */
        SDIOx->FIFO = *pWriteData;

        return HAL_OK;
    }

/**
 * @}
 */

/** @defgroup HAL_SDMMC_LL_Group3 Peripheral Control functions
 *  @brief   management functions
 *
 * @verbatim
 * ===============================================================================
 ##### Peripheral Control functions #####
 #####===============================================================================
 #####[..]
 #####This subsection provides a set of functions allowing to control the SDIO data
 #####transfers.
 #####
 #####@endverbatim
 * @{
 */

/**
 * @brief  Set SDIO Power state to ON.
 * @param  SDIOx Pointer to SDIO register base
 * @retval HAL status
 */
    HAL_StatusTypeDef SDIO_PowerState_ON( SDIO_TypeDef * SDIOx )
    {
        /* Set power state to ON */
        SDIOx->POWER = SDIO_POWER_PWRCTRL;

        return HAL_OK;
    }

/**
 * @brief  Set SDIO Power state to OFF.
 * @param  SDIOx Pointer to SDIO register base
 * @retval HAL status
 */
    HAL_StatusTypeDef SDIO_PowerState_OFF( SDIO_TypeDef * SDIOx )
    {
        /* Set power state to OFF */
        SDIOx->POWER = ( uint32_t ) 0x00000000;

        return HAL_OK;
    }

/**
 * @brief  Get SDIO Power state.
 * @param  SDIOx Pointer to SDIO register base
 * @retval Power status of the controller. The returned value can be one of the
 *         following values:
 *            - 0x00: Power OFF
 *            - 0x02: Power UP
 *            - 0x03: Power ON
 */
    uint32_t SDIO_GetPowerState( SDIO_TypeDef * SDIOx )
    {
        return( SDIOx->POWER & SDIO_POWER_PWRCTRL );
    }

/**
 * @brief  Configure the SDIO command path according to the specified parameters in
 *         SDIO_CmdInitTypeDef structure and send the command
 * @param  SDIOx Pointer to SDIO register base
 * @param  SDIO_CmdInitStruct pointer to a SDIO_CmdInitTypeDef structure that contains
 *         the configuration information for the SDIO command
 * @retval HAL status
 */
    HAL_StatusTypeDef SDIO_SendCommand( SDIO_TypeDef * SDIOx,
                                        SDIO_CmdInitTypeDef * SDIO_CmdInitStruct )
    {
        uint32_t tmpreg = 0;

        /* Check the parameters */
        assert_param( IS_SDIO_CMD_INDEX( SDIO_CmdInitStruct->CmdIndex ) );
        assert_param( IS_SDIO_RESPONSE( SDIO_CmdInitStruct->Response ) );
        assert_param( IS_SDIO_WAIT( SDIO_CmdInitStruct->WaitForInterrupt ) );
        assert_param( IS_SDIO_CPSM( SDIO_CmdInitStruct->CPSM ) );

        /* Set the SDIO Argument value */
        SDIOx->ARG = SDIO_CmdInitStruct->Argument;

        /* Set SDIO command parameters */
        tmpreg |= ( uint32_t ) ( SDIO_CmdInitStruct->CmdIndex |         \
                                 SDIO_CmdInitStruct->Response |         \
                                 SDIO_CmdInitStruct->WaitForInterrupt | \
                                 SDIO_CmdInitStruct->CPSM );

        /* Write to SDIO CMD register */
        MODIFY_REG( SDIOx->CMD, CMD_CLEAR_MASK, tmpreg );

        return HAL_OK;
    }

/**
 * @brief  Return the command index of last command for which response received
 * @param  SDIOx Pointer to SDIO register base
 * @retval Command index of the last command response received
 */
    uint8_t SDIO_GetCommandResponse( SDIO_TypeDef * SDIOx )
    {
        return ( uint8_t ) ( SDIOx->RESPCMD );
    }


/**
 * @brief  Return the response received from the card for the last command
 * @param  SDIO_RESP Specifies the SDIO response register.
 *          This parameter can be one of the following values:
 *            @arg SDIO_RESP1: Response Register 1
 *            @arg SDIO_RESP2: Response Register 2
 *            @arg SDIO_RESP3: Response Register 3
 *            @arg SDIO_RESP4: Response Register 4
 * @retval The Corresponding response register value
 */
    uint32_t SDIO_GetResponse( uint32_t SDIO_RESP )
    {
        __IO uint32_t tmp = 0;

        /* Check the parameters */
        assert_param( IS_SDIO_RESP( SDIO_RESP ) );

        /* Get the response */
        tmp = SDIO_RESP_ADDR + SDIO_RESP;

        return( *( __IO uint32_t * ) tmp );
    }

/**
 * @brief  Configure the SDIO data path according to the specified
 *         parameters in the SDIO_DataInitTypeDef.
 * @param  SDIOx Pointer to SDIO register base
 * @param  SDIO_DataInitStruct  pointer to a SDIO_DataInitTypeDef structure
 *         that contains the configuration information for the SDIO command.
 * @retval HAL status
 */
    HAL_StatusTypeDef SDIO_DataConfig( SDIO_TypeDef * SDIOx,
                                       SDIO_DataInitTypeDef * SDIO_DataInitStruct )
    {
        uint32_t tmpreg = 0;

        /* Check the parameters */
        assert_param( IS_SDIO_DATA_LENGTH( SDIO_DataInitStruct->DataLength ) );
        assert_param( IS_SDIO_BLOCK_SIZE( SDIO_DataInitStruct->DataBlockSize ) );
        assert_param( IS_SDIO_TRANSFER_DIR( SDIO_DataInitStruct->TransferDir ) );
        assert_param( IS_SDIO_TRANSFER_MODE( SDIO_DataInitStruct->TransferMode ) );
        assert_param( IS_SDIO_DPSM( SDIO_DataInitStruct->DPSM ) );

        /* Set the SDIO Data Timeout value */
        SDIOx->DTIMER = SDIO_DataInitStruct->DataTimeOut;

        /* Set the SDIO DataLength value */
        SDIOx->DLEN = SDIO_DataInitStruct->DataLength;

        /* Set the SDIO data configuration parameters */
        tmpreg |= ( uint32_t ) ( SDIO_DataInitStruct->DataBlockSize | \
                                 SDIO_DataInitStruct->TransferDir |   \
                                 SDIO_DataInitStruct->TransferMode |  \
                                 SDIO_DataInitStruct->DPSM );

        /* Write to SDIO DCTRL */
        /* CLEAR_MASK = SDIO_DCTRL_DTEN | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTMODE | SDIO_DCTRL_DBLOCKSIZE */

        MODIFY_REG( SDIOx->DCTRL, DCTRL_CLEAR_MASK, tmpreg );

        return HAL_OK;
    }

/**
 * @brief  Returns number of remaining data bytes to be transferred.
 * @param  SDIOx Pointer to SDIO register base
 * @retval Number of remaining data bytes to be transferred
 */
    uint32_t SDIO_GetDataCounter( SDIO_TypeDef * SDIOx )
    {
        return( SDIOx->DCOUNT );
    }

/**
 * @brief  Get the FIFO data
 * @param  SDIOx Pointer to SDIO register base
 * @retval Data received
 */
    uint32_t SDIO_GetFIFOCount( SDIO_TypeDef * SDIOx )
    {
        return( SDIOx->FIFO );
    }


/**
 * @brief  Sets one of the two options of inserting read wait interval.
 * @param  SDIO_ReadWaitMode SD I/O Read Wait operation mode.
 *          This parameter can be:
 *            @arg SDIO_READ_WAIT_MODE_CLK: Read Wait control by stopping SDIOCLK
 *            @arg SDIO_READ_WAIT_MODE_DATA2: Read Wait control using SDIO_DATA2
 * @retval None
 */
    HAL_StatusTypeDef SDIO_SetSDIOReadWaitMode( uint32_t SDIO_ReadWaitMode )
    {
        /* Check the parameters */
        assert_param( IS_SDIO_READWAIT_MODE( SDIO_ReadWaitMode ) );

        *( __IO uint32_t * ) DCTRL_RWMOD_BB = SDIO_ReadWaitMode;

        return HAL_OK;
    }

/**
 * @}
 */

/**
 * @}
 */

#endif /* (HAL_SD_MODULE_ENABLED) || (HAL_MMC_MODULE_ENABLED) */

/**
 * @}
 */

/**
 * @}
 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
